#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include "sboxes.h"
#include "getKfromK3.h"


/* take an unsigned hexadecimal uint32_teger less than
0xffffffffffffffff and return an array of 8 char*/
uint8_t *
int2arrayOfChar (uint64_t k)
{

  uint8_t *arr;
  arr = (uint8_t *) malloc (8 * sizeof (uint8_t));
  for (int i = 0; i < 8; i++)
    {
      arr[i] = (k & (255L << (8 * i))) >> (8 * i);
      //printf("zz%"PRIu64"\n", k&0xffL<<(i*8));
    }
  return arr;
}

/* do the reverse of the above function*/
uint64_t
arrayOfChar2int (uint8_t * arr)
{

  uint64_t k = 0;
  for (int i = 0; i < 8; i++)
    k += ((uint64_t) arr[i] << (8 * i));

  return k;
}

uint32_t
getIN (uint32_t sb[4][16], uint32_t INj[16], uint32_t ep, uint32_t cp)
{

  uint32_t delta = 0, x, F_x, F_apx;
  uint32_t k = 0;
  if (ep == 0 && cp == 0)
    return 64;
  else
    {

      uint32_t indices[64][2];
      for (int i = 0; i < 64; i++)
	{
	  indices[i][0] = (i & 1) | ((i & 32) >> 4);
	  indices[i][1] =
	    ((i & 2) >> 1) | ((i & 4) >> 1) | ((i & 8) >> 1) | ((i & 16) >> 1);
	}

      for (x = 0; x < 64; x++)
	{
	  F_x = sb[indices[x][0]][indices[x][1]];
	  F_apx = sb[indices[(ep ^ x)][0]][indices[(ep ^ x)][1]];
	  if ((F_x ^ F_apx) == cp)
	    {
	      delta += 1;
	      INj[k] = x;
	      k++;
	    }
	}
    }
  return delta;
}

uint32_t
getTestj (uint32_t sb[4][16], uint32_t counter[64],
	  uint32_t Testj[16], uint32_t e, uint32_t es, uint32_t cp)
{

  uint32_t ep, delta;
  uint32_t INj[16] = { 0 };
  ep = e ^ es;
  //fprintf (stderr, "cp: %" PRIu32 "\n", cp);
  delta = getIN (sb, INj, ep, cp);
  for (int i = 0; i < delta; i++)
    {
      Testj[i] = e ^ INj[i];
	counter[Testj[i]]++;
    }
  return delta;
}

uint32_t Calc_Pinv(uint32_t M){ // elegant code from M'hammed
	
	uint32_t res=0;

	for(int i=0; i<32; i++) 
		res^=(((M&(1<<i))>>i)<<(32-Per[31-i]));
		
	return res;
}


uint32_t // there is small bug I have not find, I use M'hammed code instead
permut (uint32_t p)
{
  uint8_t bits[32], pbits[32];
  uint32_t j = 1, res = 0;
  for (int i = 0; i < 32; i++)
    {
      bits[i] = (uint8_t) ((p & j) >> i);
      j <<= 1;
    }

  for (int i = 0; i < 32; i++)
    pbits[i] = bits[InvPer[i] - 1];

  for (int i = 0; i < 32; i++)
    {
      res = res | (((uint32_t) pbits[i]) << i);
      //printf("%u\n", res);
    }
  return res;
}

uint64_t
expand (uint32_t p)
{
  uint8_t bits[32], ebits[48];
  uint32_t j = 1;
  uint64_t res = 0;
  for (int i = 0; i < 32; i++)
    {
      bits[i] = (uint8_t) ((p & j) >> i);
      j <<= 1;
    }

  for (int i = 0; i < 48; i++)
    ebits[i] = bits[Expand[i + 1] - 1];

  for (int i = 0; i < 48; i++)
    {
      res = res | (((uint64_t) ebits[i]) << i);
      //printf("%u\n", res);
    }
  return res;
}

int
main (void)
{
  uint32_t delta;

  uint32_t Lzero[3] = { 0x748502cd, 0x48691102, 0x357418da };
  //uint32_t Rzero[3] = {0x38451097, 0x6acdff31, 0x013fec86};
  uint32_t LSzero[3] = { 0x38747564, 0x375bd31f, 0x12549847 };

  uint32_t Rtrois[3] = { 0xd8a09f10, 0xe5adc730, 0x28bbc5cf };

  uint32_t RStrois[3] = { 0x60e6d4cb, 0xac253457, 0xb23cb944 };

  uint32_t Ltrois[3] = { 0x03c70306, 0x45fa285b, 0xd8a31b2f };
  uint32_t LStrois[3] = { 0x78560a09, 0x134f7915, 0x0f317ac2 };

  uint32_t CP[3]; //= { 0x965d5b67, 0x9c9c1f56, 0xd575db2b }; 
  uint64_t E[3], ES[3];

  uint32_t counter[8][64] = { {0} };

  for (int i = 0; i < 3; i++)
    {

 	 uint32_t Testj[8][16] = {{ 0 }};
      CP[i] = Calc_Pinv(Rtrois[i] ^ RStrois[i] ^ Lzero[i] ^ LSzero[i]);
	 //printf("XOR: %"PRIx32"\n", Rtrois[i]^RStrois[i] ^ Lzero[i]^LSzero[i]);
	 //printf("XOR: %"PRIx32"\n", Calc_Pinv(0xf4b73c72));
	 //CP[0] = npermut(CP[0]);
      E[i] = expand (Ltrois[i]);
      //printf ("E0: %" PRIx64 "\n", E[i]);
      ES[i] = expand (LStrois[i]);
      //printf ("ES0: %" PRIx64 "\n", ES[i]);
      for (int j = 0; j < 8; j++)
	{
	  delta =
	    getTestj (S[j], counter[j], Testj[j],
		      (E[i] & (((uint64_t) 0x3f) << (6 * (7-j)))) >> (6 * (7-j)),
		      (ES[i] & (((uint64_t) 0x3f) << (6 * (7-j)))) >> (6 * (7-j)),
		      (CP[i] & (((uint32_t) 0xf) << (4 * (7-j)))) >> (4 * (7-j)));
	  //printf ("delta: %" PRIu32 "\t", delta);
	  /*printf ("E: %" PRIx64 "\t",
		  (E[i] & (((uint64_t) 0x3f) << (6 * (7-j)))) >> (6 * (7-j)));
	  printf ("ES: %" PRIx64 "\t",
		  (ES[i] & (((uint64_t) 0x3f) << (6 * (7-j)))) >> (6 * (7-j)));
	  printf ("CP: %" PRIx32 "\n",
		  (CP[i] & (((uint32_t) 0xf) << (4 * (7-j)))) >> (4 * (7-j)));*/
	}
    }

  /*for (int i = 0; i < 8; i++)
    for (int j = 0; j < 64; j++)
      printf ("%i  %x  %" PRIu32 "\n", i, j, counter[i][j]);*/

  //printf("%"PRIu32"\n%"PRIu32"\n", CP[0], CP[1]);       
  int8_t K3[48] = {1,0,1,1,1,1,  0,0,0,1,0,1,  0,1,0,0,1,1,  0,0,0,0,0,0,
  			0,1,1,0,0,0,  0,0,0,1,1,1,  0,0,0,1,1,1,  1,1,0,0,0,1};
  
  int8_t *K = (int8_t *) malloc(64*sizeof(int8_t));
  K = getKfromK3(K3);
  for(int i=0; i<64; i++)
  	printf("K[%i]: %"PRIi8"\n", i, K[i]);
  				


  return 0;
}
